

package raft

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"math/rand"
	"sort"
	"strings"
	"sync"
	"time"

	"go.etcd.io/etcd/raft/v3/confchange"
	"go.etcd.io/etcd/raft/v3/quorum"
	pb "go.etcd.io/etcd/raft/v3/raftpb"
	"go.etcd.io/etcd/raft/v3/tracker"
)

// None是在没有引线时使用的占位符节点ID。
const None uint64 = 0
const noLimit = math.MaxUint64

// StateType的可能值。
const (
	StateFollower StateType = iota
	StateCandidate
	StateLeader
	StatePreCandidate
	numStates
)

type ReadOnlyOption int

const (
	// 只读安全保证了
	// 与仲裁通信时只读请求的线性化。这是默认的和建议的选项。
	ReadOnlySafe ReadOnlyOption = iota
	// 基于ReadOnlyLaseBased确保了
	// 依赖leader租约的只读请求的线性化。它会受到时钟漂移的影响。
	// 如果时钟漂移是无界的，leader可能保持租约的时间比
	// 应该的时间长（时钟可以向后移动/暂停而不受任何限制）。在这种情况下，ReadIndex是不安全的。
	ReadOnlyLeaseBased
)

// CampaignType的可能值
const (
	// campaignPreElection表示正常选举的第一阶段，此时
	// Config。普雷沃特是真的。
	campaignPreElection CampaignType = "CampaignPreElection"
	// campaignElection代表正常（基于时间的）选举（当Config.PreVote为true时，选举的第二阶段
	// ）。
	campaignElection CampaignType = "CampaignElection"
	// campaignTransfer表示领导者转移的类型
	campaignTransfer CampaignType = "CampaignTransfer"
)

// ErrProposalDropped在某些情况下被忽略时返回，
// ，以便通知提议者并快速失败。
var ErrProposalDropped = errors.New("raft proposal dropped")

// lockedRand是兰德的一个小包装。兰德提供
// 多个筏组之间的同步。只有代码所需的方法
// 才会被公开（例如Intn）。
type lockedRand struct {
	mu   sync.Mutex
	rand *rand.Rand
}

func (r *lockedRand) Intn(n int) int {
	r.mu.Lock()
	v := r.rand.Intn(n)
	r.mu.Unlock()
	return v
}

var globalRand = &lockedRand{
	rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}

// 活动类型表示活动的类型
// 之所以使用字符串类型而不是uint64 
// 是因为比较和填写raft条目更简单
type CampaignType string

// StateType表示集群中节点的角色。
type StateType uint64

var stmap = [...]string{
	"StateFollower",
	"StateCandidate",
	"StateLeader",
	"StatePreCandidate",
}

func (st StateType) String() string {
	return stmap[uint64(st)]
}

// 配置包含启动筏的参数。
type Config struct {
	ID uint64	// 当前节点的ID。
	ElectionTick int	// 用于初始化raft.electionTimeout，即逻辑时钟连续推进多少次后，就会触发Follower节点的状态切换及新一轮的Leader选举。
	HeartbeatTick int	// 用于初始化ra位heartbeatTimeout，即逻辑时钟连续推进多少次后，就触发Leader节点发送心跳消息。
	Storage Storage	// 当前节点保存raft日志记录使用的存储
	Applied uint64	// 当前已经应用的记录位置（己应用的最后一条Entry记录的索引值），该值在节点重启时需要设置，否则会重新应用己经应用过Entry记录。
	MaxSizePerMsg uint64	// 用于初始化raft.maxMsgSize字段，每条消息的最大字节数，如果是math.MaxUint64则没有上限，如果是0则表示每条消息最多携带一条Entry。
	MaxCommittedSizePerReady uint64	// 最大提交每条正式per大小
	MaxUncommittedEntriesSize uint64	// 最大提交Entry大小
	MaxInflightMsgs int	// 用于初始化raftmaxlnflight，即已经发送出去且未收到响应的最大消息个数。
	CheckQuorum bool	// 是否开启CheckQuorum模式，用于初始化raft.checkQuorum字段，
	PreVote bool	// 是否开启PreVote模式，用于初始化raftpreVote宇段，
	ReadOnlyOption ReadOnlyOption	// 只读请求的处理相关
	Logger Logger
	DisableProposalForwarding bool
}

func (c *Config) validate() error {
	if c.ID == None {
		return errors.New("cannot use none as id")
	}

	if c.HeartbeatTick <= 0 {
		return errors.New("heartbeat tick must be greater than 0")
	}

	if c.ElectionTick <= c.HeartbeatTick {
		return errors.New("election tick must be greater than heartbeat tick")
	}

	if c.Storage == nil {
		return errors.New("storage cannot be nil")
	}

	if c.MaxUncommittedEntriesSize == 0 {
		c.MaxUncommittedEntriesSize = noLimit
	}

	// 默认的MaxCommittedSizePreready为MaxSizePerMsg，因为它们是
	// 以前的同一个参数。
	if c.MaxCommittedSizePerReady == 0 {
		c.MaxCommittedSizePerReady = c.MaxSizePerMsg
	}

	if c.MaxInflightMsgs <= 0 {
		return errors.New("max inflight messages must be greater than 0")
	}

	if c.Logger == nil {
		c.Logger = getLogger()
	}

	if c.ReadOnlyOption == ReadOnlyLeaseBased && !c.CheckQuorum {
		return errors.New("CheckQuorum must be enabled when ReadOnlyOption is ReadOnlyLeaseBased")
	}

	return nil
}

// 结构体raft中封装了当前节点所有的核心数据
type raft struct {
	id uint64	// 当前节点在集群中的ID
	Term uint64	// 当前任期号,如果Message的Term字段为0，则表示该消息是本地消息
	Vote uint64	// 当前任期中当前节点将选票投给了哪个节点，未投票时，该字段为None
	readStates []ReadState	// 只读请求相关
	raftLog *raftLog	// 记录本地Log,
	maxMsgSize         uint64	// 单条消息的最大字节数
	maxUncommittedSize uint64	// 没有提交的最大消息数
	prs tracker.ProgressTracker	// 封装了日志复制情况CNextlndex和Matchlndex），封装了对应Follower节点的相关信息
	// prs 各个字段
	// Match ( uint64类型）：对应Follower节点当前己经成功复制的En町记录的索引值
	// Next ( uint64类型）：对应Follower节点下一个待复制的Entry记录的索引值。
	// State( ProgressState丁ype类型）：对应Follower节点的复制状态，
	// Paused ( boo I类型）：当前Leader节点是否可以向该Progress实例对应的Follower节点发送消息。
	// PendingSnapshot ( uint64类型）：当前正在发送的快照数据信息
	// RecentActive ( bool类型）：从当前Leader节点的角度来看，该Progress实例对应的Follower节点是否存活
	// ins( *inflights类型）：记录了己经发送出去但未收到响应的消息信息。
	state StateType	// 当前节点在集群中的角色,StateFollower、StateCandidate、StateLeader和StatePreCandidat巳四种状态
	isLearner bool	// 是否是集群主节点
	msgs []pb.Message	// 缓存了当前节点等待发送的消息
	lead uint64	// 当前集群中Leader节点的ID
	leadTransferee uint64	// 用于集群中Leader节点的转移，leadTransferee记录了此次Leader角色转移的目标节点的ID。
	pendingConfIndex uint64	//
	uncommittedSize uint64	//
	readOnly *readOnly	// 只读请求相关
	electionElapsed int	// 选举计时器的指针，其单位是逻辑时钟的刻度，逻辑时钟每推进一次，该字段值就会增加1
	heartbeatElapsed int	// 心跳计时器的指针，其单位也是逻辑时钟的刻度，逻辑时钟每推进一次，该字段值就会增加1
	checkQuorum bool	// 尝试连接其他节点，未到半数，主动切换成Follower状态
	preVote     bool	// 当在PreVote过程中，有超过半数的节点响应并参与新一轮选举，则可以发起新一轮的选举。
	heartbeatTimeout int	// 心跳超时时间，当heartbeatElapsed字段值到达该值时，就会触发Leader节点发送一条心跳消息。
	electionTimeout  int	// 选举超时时间，当electionElapsed字段值到达该值时，就会触发新一轮的选举。
	randomizedElectionTimeout int	// 选举计时器的上限，当electionElapsed超过该值时即为超时。
	disableProposalForwarding bool	//
	tick func()	// 当前节点推进逻辑时钟的函数
	step stepFunc	// 当前节点收到消息时的处理函数
	logger Logger	//
	pendingReadIndexMessages []pb.Message	//
}

// 根据传入的Config实例中携带的参数创建ra丘实例并初始化raft使用到的相关组件
func newRaft(c *Config) *raft {
	// 检测参数Config中各字段的合法性
	if err := c.validate(); err != nil {
		panic(err.Error())
	}
	// 创建raftLog实例，用于记录Entry记录
	raftlog := newLogWithSize(c.Storage, c.Logger, c.MaxCommittedSizePerReady)
	// 获取raftLog.storage的初始状态（HardState和ConfState）
	// Storage的初始状态是通过本地Entry记录回放得到的
	hs, cs, err := c.Storage.InitialState()
	if err != nil {
		panic(err) // TODO（bdarnell）
	}
	// 创建raft实例，
	r := &raft{
		id:                        c.ID,	// 当前节点的ID
		lead:                      None,	// ／当前集群中Leader节点的工D，初始化时先被设立成o
		isLearner:                 false,
		raftLog:                   raftlog,	// 负责管理Entry记录的raftLog实例,每条消息的最大字节数，如采是math.MaxUint64则没有上限，如采是o则表示每条消息最多携带一条Entry
		maxMsgSize:                c.MaxSizePerMsg,
		maxUncommittedSize:        c.MaxUncommittedEntriesSize,
		prs:                       tracker.MakeProgressTracker(c.MaxInflightMsgs),
		electionTimeout:           c.ElectionTick,	// 选举起时时间
		heartbeatTimeout:          c.HeartbeatTick,	// 心跳起时时间
		logger:                    c.Logger,	// 普通日志输出
		checkQuorum:               c.CheckQuorum,	// 是否开始CheckQuorum模式
		preVote:                   c.PreVote,	// 是否开后PreVote模式
		readOnly:                  newReadOnly(c.ReadOnlyOption),	// 只读请求的相关配置
		disableProposalForwarding: c.DisableProposalForwarding,
	}
	cfg, prs, err := confchange.Restore(confchange.Changer{
		Tracker:   r.prs,
		LastIndex: raftlog.lastIndex(),
	}, cs)
	if err != nil {
		panic(err)
	}
	assertConfStatesEquivalent(r.logger, cs, r.switchToConfig(cfg, prs))

	// 根据从Storage中获取的HardState，初始化raftLog.committed字段，以及raft.Term和Vote字段
	if !IsEmptyHardState(hs) {
		r.loadState(hs)
	}
	// 如采Config中国己置了Applied，则将raftLog.applied字段重直为指定的Applied值
	// 上层模块自己的控制正确的己应用位置时使用该配置
	if c.Applied > 0 {
		raftlog.appliedTo(c.Applied)
	}
	r.becomeFollower(r.Term, None)	// 当前节点切换成Follower状态

	var nodesStrs []string
	for _, n := range r.prs.VoterNodes() {
		nodesStrs = append(nodesStrs, fmt.Sprintf("%x", n))
	}

	r.logger.Infof("newRaft %x [peers: [%s], term: %d, commit: %d, applied: %d, lastindex: %d, lastterm: %d]",
		r.id, strings.Join(nodesStrs, ","), r.Term, r.raftLog.committed, r.raftLog.applied, r.raftLog.lastIndex(), r.raftLog.lastTerm())
	return r
}

func (r *raft) hasLeader() bool { return r.lead != None }

func (r *raft) softState() *SoftState { return &SoftState{Lead: r.lead, RaftState: r.state} }

func (r *raft) hardState() pb.HardState {
	return pb.HardState{
		Term:   r.Term,
		Vote:   r.Vote,
		Commit: r.raftLog.committed,
	}
}

// raft.send（）方法会在消息发送之前对不同类型的消息进行合法性检测，然后将待发送的消息追加到raft.msg字段中
func (r *raft) send(m pb.Message) {
	if m.From == None {
		m.From = r.id	// 设置消息的发送节点ID，即当前节点ID
	}
	if m.Type == pb.MsgVote || m.Type == pb.MsgVoteResp || m.Type == pb.MsgPreVote || m.Type == pb.MsgPreVoteResp {
		// 对MsgVote,MsgVoteResp,MsgPreVote,MsgPreVoteResp消息的Term字段进行检查
		if m.Term == 0 {
			panic(fmt.Sprintf("term should be set when sending %s", m.Type))
		}
	} else {
		if m.Term != 0 {	// 对其他类型消息的Term字段位进行设置
			panic(fmt.Sprintf("term should not be set when sending %s (was %d)", m.Type, m.Term))
		}
		// 除了MsgProp和MsgReadindex两类消息（这两类消息的Term值为0,即为本地消息）之外，其他类型消息的Term字段值在这里统一设置
		if m.Type != pb.MsgProp && m.Type != pb.MsgReadIndex {
			m.Term = r.Term
		}
	}
	r.msgs = append(r.msgs, m)	// 将消息添加圭1Jr.msgs切片中等待发送
}

// Leader节点发送MsgSnap消息的过程
func (r *raft) sendAppend(to uint64) {
	r.maybeSendAppend(to, true)
}

// 主要负责向指定的目标节点发送MsgApp消息（或MsgSnap消息〉，
// 在消息发送之前会检测当前节点的状态，然后查找待发迭的Entry记录并封装成MsgApp消息，
// 之后根据对应节点的Progress.State值决定发送消息之后的操作
// Progress.State各值的含义:
// ProgressStateSnapshot状态表示Leader节点正在向目标节点发送快照数据。
// ProgressStateProbe状态表示Leader节点一次不能向目标节点发送多条消息，只能待一条消息被响应之后，才能发送下一条消息。
// 当刚刚复制完快照数据、上次MsgApp消息被拒绝（或是发送失败）或是Leader节点初始化时，都会导致目标节点的Progress切换到该状态。
// ProgressStateReplicate状态表示正常的Entry记录复制状态，Leader节点向目标节点发送完消息之后，无须等待响应，即可开始后续消息的发送。
func (r *raft) maybeSendAppend(to uint64, sendIfEmpty bool) bool {
	pr := r.prs.Progress[to]
	if pr.IsPaused() {	// 检测当前节点是否可以向目标节点发送消息，
		return false
	}
	m := pb.Message{}	// 创建待发送的消息
	m.To = to	// 设置目标节点的ID

	// 根据当前Leader节点记录的Next查找发往指定节点的Entry记录（ents）及Next索引对应的记录的Term值（term)
	// 查找待发送的消息
	term, errt := r.raftLog.term(pr.Next - 1)
	ents, erre := r.raftLog.entries(pr.Next, r.maxMsgSize)
	if len(ents) == 0 && !sendIfEmpty {
		return false
	}

	// 上述两次raftLog查找出现异常时，就会形成MsgSnap消息，将快照数据发送到指定节点，
	if errt != nil || erre != nil { // 如采上述raftLog的查找出现异常，则会尝试发送MsgSnap消息
		if !pr.RecentActive {	// 从当前的Leader节点的角度来看，目标Follower节点已经不再存活
			r.logger.Debugf("ignore sending snapshot to %x since it is not recently active", to)
			return false
		}

		m.Type = pb.MsgSnap	// 将消息类型设直成MsgSnap，为后续发送快照数据做准备
		snapshot, err := r.raftLog.snapshot()	// 获取快照数据
		// 异常检测，如采获取快照数据出现异常，则终止整个程序
		if err != nil {
			if err == ErrSnapshotTemporarilyUnavailable {
				r.logger.Debugf("%x failed to send snapshot to %x because snapshot is temporarily unavailable", r.id, to)
				return false
			}
			panic(err) // TODO（bdarnell）
		}
		if IsEmptySnap(snapshot) {
			panic("need non-empty snapshot")
		}
		m.Snapshot = snapshot	// 设置MsgSnap消息的Snapshot字段
		sindex, sterm := snapshot.Metadata.Index, snapshot.Metadata.Term	// 获取快照的相关信息
		r.logger.Debugf("%x [firstindex: %d, commit: %d] sent snapshot[index: %d, term: %d] to %x [%s]",
			r.id, r.raftLog.firstIndex(), r.raftLog.committed, sindex, sterm, to, pr)
		// 将目标Follower节点对应的Progress切换成ProgressStateSnapshot状态，
		// 其中会用Progress.PendingSnapshot字段记录快照数据的信息
		pr.BecomeSnapshot(sindex)
		r.logger.Debugf("%x paused sending replication messages to %x [%s]", r.id, to, pr)
	} else {
		m.Type = pb.MsgApp	// 设置消息类型
		m.Index = pr.Next - 1	// 设置MsgApp消息的Index字段
		m.LogTerm = term	// 设置MsgApp消息的LogTerm字段
		m.Entries = ents	// 设置消息携带的Entry记录集合
		m.Commit = r.raftLog.committed
		if n := len(m.Entries); n != 0 {
			switch pr.State {	// 根据目标节点对应的Progress.State状态决定其发送消息后的行为
			case tracker.StateReplicate:	// StateReplicate状态下的处理
				last := m.Entries[n-1].Index
				pr.OptimisticUpdate(last)	// 更新目标节点对应的Nextf直（这里不会更新Match位）
				pr.Inflights.Add(last)	// 记录已发送但是未收到响应的消息
			case tracker.StateProbe:	// StateProbe状态下的处理
				// 消息发送后，就将Progress.ProbeSent字段设置成true，暂停后续消息的发送
				pr.ProbeSent = true
			default:
				r.logger.Panicf("%x is sending append in unhandled state %s", r.id, pr.State)
			}
		}
	}
	// 发送前面创建的MsgApp消息，raft.send（）方法会设置MsgApp消息的Term字段佳，
	// 并将其追加到raft.msgs中等待发送
	r.send(m)
	return true
}

// 向指定的节点发送MsgBeat消息。
func (r *raft) sendHeartbeat(to uint64, ctx []byte) {
	// 注意MsgHeartbeat消息中Commit字段的设置，这主要是因为在发送该MsgHeartbeat消息时，
	// Follower节点并不一定已经收到了全部已提交的Entry记录
	commit := min(r.prs.Progress[to].Match, r.raftLog.committed)
	m := pb.Message{
		To:      to,
		Type:    pb.MsgHeartbeat,
		Commit:  commit,
		Context: ctx,
	}

	r.send(m)
}

// ra位bcastAppend（）方法主要负责向集群中的其他节点发送MsgApp消息（或MsgSnap消息〉，
func (r *raft) bcastAppend() {
	r.prs.Visit(func(id uint64, _ *tracker.Progress) {
		if id == r.id {	// 过滤当前节点本身，只向集群中其他节点发送消息
			return
		}
		r.sendAppend(id)	// 向指定节点发送消息
	})
}

// 通过调用raft.bcastHeartbeatWithCtx（）方法向集群中其他节点发送MsgHeartbeat消息。
func (r *raft) bcastHeartbeat() {
	lastCtx := r.readOnly.lastPendingRequestCtx()
	if len(lastCtx) == 0 {
		r.bcastHeartbeatWithCtx(nil)
	} else {
		r.bcastHeartbeatWithCtx([]byte(lastCtx))
	}
}

// 向集群中其他节点发送MsgHeartbeat消息
func (r *raft) bcastHeartbeatWithCtx(ctx []byte) {
	r.prs.Visit(func(id uint64, _ *tracker.Progress) {
		if id == r.id {	// 过滤当前节点自身
			return
		}
		r.sendHeartbeat(id, ctx)	// 向指定的节点发送MsgBeat消息
	})
}

func (r *raft) advance(rd Ready) {
	r.reduceUncommittedSize(rd.CommittedEntries)

	// 如果应用了条目（或快照），请将光标更新为
	// 下一个就绪。请注意，如果当前硬状态包含一个新提交索引
	// ，这并不意味着由于按大小进行提交分页，我们也在应用所有新条目。
	if newApplied := rd.appliedCursor(); newApplied > 0 {
		oldApplied := r.raftLog.applied
		r.raftLog.appliedTo(newApplied)

		if r.prs.Config.AutoLeave && oldApplied <= r.pendingConfIndex && newApplied >= r.pendingConfIndex && r.state == StateLeader {
			// 如果当前配置（以及最新配置，至少在本领导任期内）
			// 应自动左移，请立即启动该配置。我们使用一个
			// nil数据，它将解组为一个空的ConfChangeV2，并且具有
			// /的优点，即appendEntry永远不能根据其大小拒绝它
			// （注册为零）。
			ent := pb.Entry{
				Type: pb.EntryConfChangeV2,
				Data: nil,
			}
			// 这项提案不可能被否决。
			if !r.appendEntry(ent) {
				panic("refused un-refusable auto-leaving ConfChangeV2")
			}
			r.pendingConfIndex = r.raftLog.lastIndex()
			r.logger.Infof("initiating automatic transition out of joint configuration %s", r.prs.Config)
		}
	}

	if len(rd.Entries) > 0 {
		e := rd.Entries[len(rd.Entries)-1]
		r.raftLog.stableTo(e.Index, e.Term)
	}
	if !IsEmptySnap(rd.Snapshot) {
		r.raftLog.stableSnapTo(rd.Snapshot.Metadata.Index)
	}
}

// 如果该Entry记录己经复制到了半数以上的节点中，则在raft.maybeCommit（）方法中会尝试将其提交。
// 除了appendEntry（）方法，在Leader节点每次收到MsgAppResp消息时也会调用maybeCommit（）方法
func (r *raft) maybeCommit() bool {
	mci := r.prs.Committed()
	// 更新raftLog.committed字段，完成提交
	return r.raftLog.maybeCommit(mci, r.Term)
}

// 在reset（）方法中会重置ra位实例的多个字段
func (r *raft) reset(term uint64) {
	if r.Term != term {
		r.Term = term	// 重置Term字R
		r.Vote = None	// 重置Vote字段
	}
	r.lead = None	// 清空lead字段
	// 重直选举计时器和心跳计时器
	r.electionElapsed = 0
	r.heartbeatElapsed = 0
	r.resetRandomizedElectionTimeout()	// 重直选举计时器的过期时间（随机值）

	r.abortLeaderTransfer()	// ；青空leadTransferee

	r.prs.ResetVotes()	// 重直votes字段
	// 重直prs，其中每个Progress中的Next设置为raftLog.lastindex
	r.prs.Visit(func(id uint64, pr *tracker.Progress) {
		*pr = tracker.Progress{
			Match:     0,
			Next:      r.raftLog.lastIndex() + 1,
			Inflights: tracker.NewInflights(r.prs.MaxInflight),
			IsLearner: pr.IsLearner,
		}
		if id == r.id {	// 将当前节点对应的prs.Match设置成lastIndex
			pr.Match = r.raftLog.lastIndex()
		}
	})

	r.pendingConfIndex = 0	// 清空pe口dingConfIndex字段
	r.uncommittedSize = 0
	r.readOnly = newReadOnly(r.readOnly.option)	// 只读请求的相关设立
}

// raftappendEntry（）方法，它的主要操作步骤如下：
// 1）设置待追加的Entry记录的Term值和Index值。
// 2）向当前节点的raftLog中追加Entry记录。
// 3）更新当前节点对应的Progress实例。
// 4）尝试提交Entr记录，即修改raftLog.committed字段的值。
func (r *raft) appendEntry(es ...pb.Entry) (accepted bool) {
	li := r.raftLog.lastIndex()	// 获取raftLog中最后一条记录的索引值
	for i := range es {	// 更新待追加记录的Term值和索引值
		es[i].Term = r.Term	// Entry记录的Term指定为当前Leader节点的任期号
		es[i].Index = li + 1 + uint64(i)	// 为日志记录指定index
	}
	//
	if !r.increaseUncommittedSize(es) {
		r.logger.Debugf(
			"%x appending new entries to log would exceed uncommitted entry size limit; dropping proposal",
			r.id,
		)
		return false
	}
	// 向raftLog中追加记录
	li = r.raftLog.append(es...)
	// 更新当前节点对应的Progress，主要是更新Next和Match
	r.prs.Progress[r.id].MaybeUpdate(li)
	// 尝试提交Entry记录
	r.maybeCommit()
	return true
}

// 当节点变成Follower状态之后，会周期性地调用ra丘tickElection（）方法推进electionElapsed并检测是否超时
func (r *raft) tickElection() {
	r.electionElapsed++	// 递增elect工0口Elapsed
	// promotable（）方法会检查prs字段中是否还存在当前节点对应的Progress实例，
	// 这是为了检测当前节点是否被从集群中移除了
	// pastElectionTimeout（）方法是检测当前的选举计时器是否超时（r.electionElapsed>= r.randomizedElectionTimeout)
	if r.promotable() && r.pastElectionTimeout() {
		r.electionElapsed = 0	// 重置electionElapsed
		if err := r.Step(pb.Message{From: r.id, Type: pb.MsgHup}); err != nil {	// 发起选举
			r.logger.Debugf("error occurred during election: %v", err)
		}
	}
}

// MsgBeati肖息和MsgCheckQuorum消息
// Leader节点除了向集群中其他Follower节点发送MsgApp消息，还会向这些Follower节点发送MsgBeat消息。
// MsgBeat消息的主要作用是探活，当Follower节点收到MsgBeat消息时会重置其选举计时器，从而防止Follower节点发起新一轮选举。
func (r *raft) tickHeartbeat() {
	r.heartbeatElapsed++	// 递增心跳计时器
	r.electionElapsed++	// 递增选举计时器

	if r.electionElapsed >= r.electionTimeout {
		r.electionElapsed = 0	// 重直选举计时器，Leader节点不会主动发起选举
		if r.checkQuorum {	// 进行多数检测
			// 当选举计时器超过electionTimeout时，会触发一次checkQuorum操作
			// 该操作并不会发送网络消息（虽然这里会通过raft.Step（）方法处理消息），
			// 它只是检测当前节点是否与集群中的大部分节点联通
			if err := r.Step(pb.Message{From: r.id, Type: pb.MsgCheckQuorum}); err != nil {
				r.logger.Debugf("error occurred during checking sending heartbeat: %v", err)
			}
		}
		// 选举计时器处于electionTimeout-randomizedElectionTimeout时间段时，
		// 不能进行Leader节点的转移
		if r.state == StateLeader && r.leadTransferee != None {
			r.abortLeaderTransfer()	// 清空raft.leadTransferee字段，放弃转移
		}
	}

	if r.state != StateLeader {	// 检测当前节点是否为Leader节点
		return
	}

	if r.heartbeatElapsed >= r.heartbeatTimeout {	// 心跳计时器起时
		r.heartbeatElapsed = 0	// 重直心跳计时器
		// 发送MsgBeat消息
		if err := r.Step(pb.Message{From: r.id, Type: pb.MsgBeat}); err != nil {
			r.logger.Debugf("error occurred during checking sending heartbeat: %v", err)
		}
	}
	// MsgCheckQuorum消息和MsgBeat消息的Term字段都为0，所以它们也属于本地消息的一种。
	//raft.Step（）方法并没有对MsgCheckQuorum消息和MsgBeat消息进行特殊的处理，其主要处理是在raft.stepLeader（）方法中
}

// raft在特定的场景下（前面介绍Raft协议时己提到过这些场景，例如，选举计时器超时时，
// Follower节点切换到PreCandidate状态等）会通过调用become＊（）方法切换成指定状态的节点
// 在newRaft（）函数中完成初始化之后，会调用becomeFollower（）方法将节点切换成Follower状态
func (r *raft) becomeFollower(term uint64, lead uint64) {
	//将step字段设置成stepFollower,stepFollower（）函数中封装了Follower节点处理消息的行为
	r.step = stepFollower
	r.reset(term)	// 重笠raft实例Term,Vote等字段
	r.tick = r.tickElection	// 将tick字段设立成tickElection函数
	r.lead = lead	// 设置当前集群的Leader节点
	r.state = StateFollower	// 设立当前节点的角色
	r.logger.Infof("%x became follower at term %d", r.id, r.Term)
}

// 当节点可以连接到集群中半数以上的节点时，会调用becomeCandidate（）方法切换到Candidate状态
func (r *raft) becomeCandidate() {
	// 检测当前节点的状态，禁止直接从Leader状态切换到PreCand工date状态
	if r.state == StateLeader {
		panic("invalid transition [leader -> candidate]")
	}
	// 将step字段设置成stepCandidate,stepCandidate （）函数中封装了Cand工date节点处理消息的行为
	r.step = stepCandidate
	// 重直raft实伊lTerm、Vote等字段，注意，这里的Term已经递增
	r.reset(r.Term + 1)
	r.tick = r.tickElection
	r.Vote = r.id	// 在此次选举中，Candidate节点会将选票投给自己
	r.state = StateCandidate	// 修改当前节点的角色
	// 注意：切换成Cand工date状态之后，raft.Leader字段为None，这与其他状态不一样
	r.logger.Infof("%x became candidate at term %d", r.id, r.Term)
}

// 如果当前集群开启了PreVote模式，当Follower节点的选举计时器超时时，会先调用becomePreCandidate（）方法切换到PreCandidate状态，
func (r *raft) becomePreCandidate() {
	// 检测当前节点的状态，禁止直接从Leader状态切换到PreCandidate状态
	if r.state == StateLeader {
		panic("invalid transition [leader -> pre-candidate]")
	}
	// 将step字段设置成stepCandidate,
	// stepCandidate（）函数中封装了PreCandidate节点处理消息的行为
	r.step = stepCandidate
	r.prs.ResetVotes()
	r.tick = r.tickElection
	r.lead = None
	r.state = StatePreCandidate	// 修改当前节点的角色
	r.logger.Infof("%x became pre-candidate at term %d", r.id, r.Term)
}

// 当Candidate节点得到集群中半数以上节点的选票时，会调用becomeLeader（）方法切换成Leader状态
func (r *raft) becomeLeader() {
	// 检测当前节点的状态，禁止从Follower状态切换成Leader状态
	if r.state == StateFollower {
		panic("invalid transition [follower -> leader]")
	}
	// 将step字段设置成stepLeader,stepLeader （）函数中封装了Leader节点处理消息的行为
	r.step = stepLeader
	r.reset(r.Term)	// 重直raft实例Term、Vote等字段
	r.tick = r.tickHeartbeat	// 将tick字段设置成tickHeartbeat函数
	r.lead = r.id	// 将leader字段当前节点的id
	r.state = StateLeader	// 更新当前节点的角色

	r.prs.Progress[r.id].BecomeReplicate()
	// 设置 pendingConfIndex
	r.pendingConfIndex = r.raftLog.lastIndex()

	// 	向当前节点的raftLog中追加一条空的Entry记录
	emptyEnt := pb.Entry{Data: nil}
	if !r.appendEntry(emptyEnt) {
		r.logger.Panic("empty entry was dropped")
	}

	r.reduceUncommittedSize([]pb.Entry{emptyEnt})
	r.logger.Infof("%x became leader at term %d", r.id, r.Term)
}

// step()消息的进一步处理
func (r *raft) hup(t CampaignType) {
	// 只有非Leader状态的节点才会处理MsgHup消息
	if r.state == StateLeader {	// 如果当前节点已经是Leader状态，则仅仅输出一条Debug日志
		r.logger.Debugf("%x ignoring MsgHup because already leader", r.id)
		return
	}

	if !r.promotable() {	// 检测当前节点是否已被移出当前集群
		r.logger.Warningf("%x is unpromotable and can not campaign", r.id)
		return
	}
	// 获取raftLog中已捉交但未应用（lipapplied～committed）的Entry记录
	ents, err := r.raftLog.slice(r.raftLog.applied+1, r.raftLog.committed+1, noLimit)
	if err != nil {
		r.logger.Panicf("unexpected error getting unapplied entries (%v)", err)
	}
	// 检测是否有未应用的EntryConfChange记录，如果有就放弃发起选举的机会
	if n := numOfPendingConf(ents); n != 0 && r.raftLog.committed > r.raftLog.applied {
		r.logger.Warningf("%x cannot campaign at term %d since there are still %d pending configuration changes to apply", r.id, r.Term, n)
		return
	}

	r.logger.Infof("%x is starting a new election at term %d", r.id, r.Term)
	// 即使当前集群开启了preVote模式，目标Follower节点也会直接发起新一轮选举，
	r.campaign(t)
}

// 在raftcampaign（）方法中，除了完成状态切换，还会向集群中的其他节点发送相应类型的消息，
// 例如，如果当前Follower节点要切换成Pr巳Candidate状态，则会发送MsgPreVote消息
func (r *raft) campaign(t CampaignType) {
	if !r.promotable() {
		r.logger.Warningf("%x is unpromotable; campaign() should have been called", r.id)
	}
	// 在该方法的最后，会发送一条消息，这里term和voteMsg分别用于确定该消息的Term值和类型
	var term uint64
	var voteMsg pb.MessageType
	if t == campaignPreElection {	// 切换的目标状态是PreCandidate
		// 将当前节点切换成PreCandidate状态，
		r.becomePreCandidate()
		voteMsg = pb.MsgPreVote	// 确定最后发送的消息是MsgPreVote类型
		// 确定最后发送消息的Term值，这里只是增加了消息的Term值，并未增加raft.Term字段的位
		term = r.Term + 1
	} else {	// 切换的目标状态是Candidate
		// 将当前节点切换成Candidate状态，becomeCandidate（）方法中会
		// 增加raft.Term字段的值，并将当前节点的选票投给自身
		r.becomeCandidate()
		voteMsg = pb.MsgVote	// 确定最后发送的消息是MsgPreVote类型
		term = r.Term	// 确定最后发送消息的Term值
	}
	// 统计当前节点收到的选票，并统计其得票数是否超过半数，这次检测王妥是为单节点设置的
	if _, _, res := r.poll(r.id, voteRespMsgType(voteMsg), true); res == quorum.VoteWon {
		// 当得到足够的选票时，则将PreCandidate状态的节点切换成Candidate状态，
		// Candidate状态的节点则切换成Leader状态
		if t == campaignPreElection {
			r.campaign(campaignElection)
		} else {
			r.becomeLeader()
		}
		return
	}
	var ids []uint64
	{
		idMap := r.prs.Voters.IDs()
		ids = make([]uint64, 0, len(idMap))
		for id := range idMap {
			ids = append(ids, id)
		}
		sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
	}
	for _, id := range ids {	// 状态切换完成之后，当前节点会向集群中所有节点发送指定类型的消息
		if id == r.id {	// 跳过当前节点自身
			continue
		}
		r.logger.Infof("%x [logterm: %d, index: %d] sent %s request to %x at term %d",
			r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), voteMsg, id, r.Term)

		var ctx []byte
		// 在进行Leader节点转移时，MsgPreVote或MsgVote消息会在Context字段中设立该特殊值
		if t == campaignTransfer {
			ctx = []byte(t)
		}
		// 发送指定类型的消息，其中Index和LogTerm分别是当前节点的raftLog中最后一条消息的Index值和Term值
		r.send(pb.Message{Term: term, To: id, Type: voteMsg, Index: r.raftLog.lastIndex(), LogTerm: r.raftLog.lastTerm(), Context: ctx})
	}
}

// 在raft.poll（）方法中会将收到的投票结果记录到raft.votes字段中，之后通过统计该字段从而确定该节点的得票数
func (r *raft) poll(id uint64, t pb.MessageType, v bool) (granted int, rejected int, result quorum.VoteResult) {
	if v {
		r.logger.Infof("%x received %s from %x at term %d", r.id, t, id, r.Term)
	} else {
		r.logger.Infof("%x received %s rejection from %x at term %d", r.id, t, id, r.Term)
	}
	r.prs.RecordVote(id, v)	// 记录集群中其他节点的投票结采
	return r.prs.TallyVotes()	// 统计投票结果并返回
	// 当PreCandidate状态节点收到半数以上的投票时，会通过rcampaign（）方法发起正式选举，
	// 其中会通过raft.becomeCandidate（）方法将当前节点切换成Candidate状态，井向剩余其他节点发送MsgVot巳消息
}

// raft.Step()方法是etcd刊负模块处理各类消息的入口，
// raft.Step（）方法主要分为两部分：
// 第一部分是根据Term值对消息进行分类处理，
// 第二部分是根据消息的类型进行分类处理
func (r *raft) Step(m pb.Message) error {
	// 首先根据消息的Term值进行分类处理
	switch {
	// 对本地消息并没有做什么处理，这里介绍的MsgHup消息Term｛直为0，就是本地消息的一种;
	// MsgProp消息和MsgReadindex消息也是本地消息，其Term位也是o
	case m.Term == 0:
	case m.Term > r.Term:
		// 对MsgVote和MsgPreVote两种类型消息进行处理
		if m.Type == pb.MsgVote || m.Type == pb.MsgPreVote {
			// 根据消息的Context字段判断收到的MsgPreVote（或MsgVote）消息是否为Leader
			// 节点转移场景下产生的，如果是，则强制当前节点参与本次预选（或选举）
			force := bytes.Equal(m.Context, []byte(campaignTransfer))
			// 通过一系列条件判断当前节点是否参与此次选举（或预选），其中主妥检测集群是否开启
			// 了CheckQuorum模式、当前节点是否有已知的Lead节点，以及其选举计时器的时间
			inLease := r.checkQuorum && r.lead != None && r.electionElapsed < r.electionTimeout
			if !force && inLease {
				r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] ignored %s from %x [logterm: %d, index: %d] at term %d: lease is not expired (remaining ticks: %d)",
					r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term, r.electionTimeout-r.electionElapsed)
				return nil	// 当前节点不参与此次选举
			}
		}
		switch {	// 当前节点会根据消息类型决定是否切换状态
		case m.Type == pb.MsgPreVote:	// 收到MsgPreVote消息时，不会引起当前节点的状态切换
		case m.Type == pb.MsgPreVoteResp && !m.Reject:
		default:
			r.logger.Infof("%x [term: %d] received a %s message with higher term from %x [term: %d]",
				r.id, r.Term, m.Type, m.From, m.Term)
			if m.Type == pb.MsgApp || m.Type == pb.MsgHeartbeat || m.Type == pb.MsgSnap {
				r.becomeFollower(m.Term, m.From)
			} else {
				r.becomeFollower(m.Term, None)	// 将当前节点切换成Follower状态
			}
		}

	case m.Term < r.Term:
		if (r.checkQuorum || r.preVote) && (m.Type == pb.MsgHeartbeat || m.Type == pb.MsgApp) {

			r.send(pb.Message{To: m.From, Type: pb.MsgAppResp})
		} else if m.Type == pb.MsgPreVote {

			r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected %s from %x [logterm: %d, index: %d] at term %d",
				r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
			r.send(pb.Message{To: m.From, Term: r.Term, Type: pb.MsgPreVoteResp, Reject: true})
		} else {
			// 忽略其他案例
			r.logger.Infof("%x [term: %d] ignored a %s message with lower term from %x [term: %d]",
				r.id, r.Term, m.Type, m.From, m.Term)
		}
		return nil
	}

	// 根据Message的Type进行分类处攻
	switch m.Type {	// 根据消息类型进行分类处理
	case pb.MsgHup:	// 这里针对MsgHup类型的消息进行处理
		if r.preVote {	// 检测当前集群是否开启了PreVote模式，如采开启了，则允切换到通过hup调用raft.campaign（）方法切换当前节点的角色，发起PreVote
			r.hup(campaignPreElection)
		} else {
			r.hup(campaignElection)
		}

	// MsgPreVote消息的处理过程：
	// raft.Step（）方法首先检测该MsgPreVote消息是否为Leader节点迁移时发出的消息及其他合法性检测，
	// 决定当前节点是否参与此次选举；之后当前节点会根据自身的状态决定是否将其选票投给MsgPreVote消息的发送节点。
	case pb.MsgVote, pb.MsgPreVote:	// 对MsgVote和MsgPreVote消息的处理
		canVote := r.Vote == m.From ||
			(r.Vote == None && r.lead == None) ||
			(m.Type == pb.MsgPreVote && m.Term > r.Term)
		// 当前节点在参与预选时，会综合下面几个条件决定是否投票
		// 1、当前节点是否已经投过票
		// 2、MsgPreVote消息发送者的任期号是否更大
		// 3、当前节点投票给了对方节点
		// 4、MsgPreVote消息发送者的raftLog中是否包含当前节点的全部Entry记录，isUpToDate（）方法
		if canVote && r.raftLog.isUpToDate(m.Index, m.LogTerm) {
			r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] cast %s for %x [logterm: %d, index: %d] at term %d",
				r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
			// 将票才是给MsgPreVote消息的发送节点
			r.send(pb.Message{To: m.From, Term: m.Term, Type: voteRespMsgType(m.Type)})
			if m.Type == pb.MsgVote {
				r.electionElapsed = 0
				r.Vote = m.From
			}
		} else {
			r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected %s from %x [logterm: %d, index: %d] at term %d",
				r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
			// 不满足上述投赞同票条件时，当前节点会返回拒绝票(即响应消息中的Reject字段会设立成true)
			r.send(pb.Message{To: m.From, Term: r.Term, Type: voteRespMsgType(m.Type), Reject: true})
		}

	// MsgVote消息的处理过程中，集群中其他节点己经切换成了Follower状态，
	// 并且它们自身记录的Term值与该任期中Leader节点维护的Term值相同。
	// 当它们收到当前Leader节点发来的MsgApp消息时，也是交由raft.Step（）方法处理的
	default:	// 对于其他类型消息的处理
		err := r.step(r, m)	// 当前节点是Follower状态，raft.step字段指向stepFollower（）函数
		if err != nil {
			return err
		}
	}
	return nil
}

type stepFunc func(r *raft, m pb.Message) error

// Leader节点，当其收到集群中其他Follower节点发迭的MsgAppResp响应消息之后，
// 也是交由raft.Step（）方法进行处理的，其中会调用raft.step字段指向的StepLeader() 函数进行处理的
func stepLeader(r *raft, m pb.Message) error {
	// 下面主妥注册MsgBeat、MsgCheckQuorum、MsgProp、MsgReadindex消息，这些消息无须处理
	// 消息的From字段和消息发送者对应的Progress实例可以直接进行处理
	switch m.Type {
	case pb.MsgBeat:	// MsgBeat消息的处理
		r.bcastHeartbeat()	// 向所有节点发送信心跳
		return nil
	case pb.MsgCheckQuorum:	// MsgCheckQuorum消息的处理
		if pr := r.prs.Progress[r.id]; pr != nil {
			pr.RecentActive = true
		}
		// 用于检测当前Leader节点是否与集群中大部分节点连通，如果不连同，则切换成Follower状态
		if !r.prs.QuorumActive() {
			r.logger.Warningf("%x stepped down to follower since quorum is not active", r.id)
			r.becomeFollower(r.Term, None)
		}
		r.prs.Visit(func(id uint64, pr *tracker.Progress) {
			if id != r.id {
				pr.RecentActive = false
			}
		})
		return nil
	case pb.MsgProp:
		// ／检测MsgProp消息是否携带了Entry记录，如果未携带，则输出异常日志并终止程序
		if len(m.Entries) == 0 {
			r.logger.Panicf("%x stepped empty MsgProp", r.id)
		}
		// 检测当前节点是否被移出集群，如果当前节点以Leader状态被移出集群，则不再处理MsgProp消息
		if r.prs.Progress[r.id] == nil {
			return ErrProposalDropped
		}
		// 检测当前是否正在进行Leader节点的转移，不再处理MsgProp消息
		if r.leadTransferee != None {
			r.logger.Debugf("%x [term %d] transfer leadership to %x is in progress; dropping proposal", r.id, r.Term, r.leadTransferee)
			return ErrProposalDropped
		}

		for i := range m.Entries {	// 边历MsgProp消息携带的全部Entry记录
			e := &m.Entries[i]
			var cc pb.ConfChangeI
			// 如果存在EntryConfChange类型的Entry记录，
			if e.Type == pb.EntryConfChange {
				var ccc pb.ConfChange
				if err := ccc.Unmarshal(e.Data); err != nil {
					panic(err)
				}
				cc = ccc
			} else if e.Type == pb.EntryConfChangeV2 {
				var ccc pb.ConfChangeV2
				if err := ccc.Unmarshal(e.Data); err != nil {
					panic(err)
				}
				cc = ccc
			}
			if cc != nil {
				alreadyPending := r.pendingConfIndex > r.raftLog.applied
				alreadyJoint := len(r.prs.Config.Voters[1]) > 0
				wantsLeaveJoint := len(cc.AsV2().Changes) == 0

				var refused string
				if alreadyPending {
					refused = fmt.Sprintf("possible unapplied conf change at index %d (applied to %d)", r.pendingConfIndex, r.raftLog.applied)
				} else if alreadyJoint && !wantsLeaveJoint {
					refused = "must transition out of joint config first"
				} else if !alreadyJoint && wantsLeaveJoint {
					refused = "not in joint state; refusing empty conf change"
				}

				if refused != "" {
					r.logger.Infof("%x ignoring conf change %v at config %s: %s", r.id, cc, r.prs.Config, refused)
					m.Entries[i] = pb.Entry{Type: pb.EntryNormal}
				} else {
					r.pendingConfIndex = r.raftLog.lastIndex() + uint64(i) + 1
				}
			}
		}
		// 将上述Entry记录追加到当前节点的raftLog中
		if !r.appendEntry(m.Entries...) {
			return ErrProposalDropped
		}
		// 通过MsgApp消息向集群中其他节点复制Entry记录，
		r.bcastAppend()
		return nil
	case pb.MsgReadIndex:
		if r.prs.IsSingleton() {	// 单节点的情况
			if resp := r.responseToReadIndexReq(m, r.raftLog.committed); resp.To != None {
				r.send(resp)
			}
			return nil
		}
		if !r.committedEntryInCurrentTerm() {
			r.pendingReadIndexMessages = append(r.pendingReadIndexMessages, m)
			return nil
		}

		sendMsgReadIndexResponse(r, m)

		return nil
	}

	// 根据消息的From字段获取对应的Progress实例，为后面的消息处理做准备
	pr := r.prs.Progress[m.From]
	if pr == nil {	// 没有操作到对反的Progress实例（该节点可能已从集群中删除）时，直接返回
		r.logger.Debugf("%x no progress available for %x", r.id, m.From)
		return nil
	}
	switch m.Type {
	case pb.MsgAppResp:
		// 更新对应Progress实例的RecentActive字段，从Leader节点的角度来看，MsgAppResp消息的发送节点还是存活的
		pr.RecentActive = true	// 回消息的节点活着

		if m.Reject {	// MsgApp 消息被拒绝
			r.logger.Debugf("%x received MsgAppResp(rejected, hint: (index %d, term %d)) from %x for index %d",
				r.id, m.RejectHint, m.LogTerm, m.From, m.Index)
			nextProbeIdx := m.RejectHint
			if m.LogTerm > 0 {
				nextProbeIdx = r.raftLog.findConflictByTerm(m.RejectHint, m.LogTerm)
			}
			// 通过MsgAppResp消息携带的信息及对应的Progress状态，重新设立其Next直
			if pr.MaybeDecrTo(m.Index, nextProbeIdx) {
				r.logger.Debugf("%x decreased progress of %x to [%s]", r.id, m.From, pr)
				// 如果对应的Progress处于ProgressStateReplicate状态，则切换成
				// ProgressStateProbe状态，试探Follower的匹配位置（这里的试探是指发送一条消息并
				// 等待其相应之后，再发送后续的消息）
				if pr.State == tracker.StateReplicate {
					pr.BecomeProbe()
				}
				// 再次向对应Follower节点发送MsgApp消息，在sendAppend()
				// 方法中会将对应的Progress.pause字段设立为true，从而暂停后续消息的发送，从而实现前面说的“试探”的效果
				r.sendAppend(m.From)
			}
		} else {	// Leader之前发送的MsgApp消息被Follower成功接收了
			oldPaused := pr.IsPaused()
			// MsgAppResp消息的Index字段是对应Follower节点raftLog中最后一条Entry记录的索引，
			// 这里会根据该值更新其对应Progress实例的Match和Next,
			if pr.MaybeUpdate(m.Index) {
				switch {
				case pr.State == tracker.StateProbe:
					// 一旦MsgApp被Follower节点接收，则表示已经找到其正确的Next和Match,
					// 不必再进行“试探”，这里将对应的Progress.state切换成ProgressStateReplicate
					pr.BecomeReplicate()
				case pr.State == tracker.StateSnapshot && pr.Match >= pr.PendingSnapshot:
					r.logger.Debugf("%x recovered from needing snapshot, resumed sending replication messages to %x [%s]", r.id, m.From, pr)
					// 之前由于某些原因，Leader节点通过发送快照的方式恢复Follower节点，但在发送
					// MsgSnap消息的过程中，Follower节点恢复，并正常接收了Leader节点的MsgApp消息，
					// 此时会丢弃MsgSnap消息，并开始“试探”该Follower节点正确的Match和Next值
					pr.BecomeProbe()
					pr.BecomeReplicate()
				case pr.State == tracker.StateReplicate:
					// 之前向某个Follower节点发送MsgApp消息时，会将其相关信息保存到对应的
					// Progress.ins中，在这里收到相应的MsgAppResp响应之后，会将其从ins中删除，
					// 这样可以实现了限流的效采，避免网络出现延迟时，继续发送消息，从而导致网络更加拥堵
					pr.Inflights.FreeLE(m.Index)
				}

				// 收到一个Follower节点的MsgAppResp消息之后，除了修改相应的Match和Next值，
				// 还会尝试更新raftLog.committed，因为有些Entry记录可能在此次复制中被保存到了半数以上的节点中，
				if r.maybeCommit() {
					// 已提交的索引已针对该术语进行了更新，因此安全地响应挂起的读取索引请求
					releasePendingReadIndexMessages(r)
					// 向所有节点发送MsgApp消息，注意，此次MsgApp消息的Commit字段与上次MsgApp消息已经不同，
					r.bcastAppend()
				} else if oldPaused {	// 之前是pause状态，现在可以任性地发消息了
					// 之前Leader节点暂停向该Follower节点发送消息，收到MsgAppResp消息后，
					// 在上述代码中已经重立了相应状态，所以可以继续发送MsgApp消息
					r.sendAppend(m.From)
				}

				for r.maybeSendAppend(m.From, false) {
				}
				// 当收到目标Follower节点的MsgAppResp消息并且两者的raftLog完全匹配，
				// 则发送MsgTimeoutNow消息
				if m.From == r.leadTransferee && pr.Match == r.raftLog.lastIndex() {
					r.logger.Infof("%x sent MsgTimeoutNow to %x after received MsgAppResp", r.id, m.From)
					r.sendTimeoutNow(m.From)
				}
			}
		}
	case pb.MsgHeartbeatResp:
		pr.RecentActive = true	// 更新对应Progress.RecentActive，表示Follow节点与自己遥远
		pr.ProbeSent = false	// 重直Progress.ProbeSent字段，表示可以继续向Follower节点发送消息

		//
		if pr.State == tracker.StateReplicate && pr.Inflights.Full() {
			pr.Inflights.FreeFirstOne()	// 释放inflights中第一个消息，这样就可以开始后续消息的发送
		}
		// 当Leader节点收到Follower节点的MsgHeartbeat消息之后，会比较对应的Match位与Leader节点的raftLog，
		// 从而判断Follower节点是否已拥有了全部的Entry记录
		if pr.Match < r.raftLog.lastIndex() {
			r.sendAppend(m.From)	// 通过向指定节点发送MsgApp消息完成Entry记录的复制
		}
		// 只读请求相关的MsgHeartbeatResp消息
		if r.readOnly.option != ReadOnlySafe || len(m.Context) == 0 {
			return nil
		}
		// 统计目前为止响应上述携带消息ID的MsgHeartbeat消息的节点个数
		if r.prs.Voters.VoteResult(r.readOnly.recvAck(m.From, m.Context)) != quorum.VoteWon {
			return nil
		}
		// 响应节点超过半数之后，会；青空readOnly中指定消息ID及其之前的所有相关记录
		rss := r.readOnly.advance(m)
		for _, rs := range rss {
			if resp := r.responseToReadIndexReq(rs.req, rs.index); resp.To != None {
				r.send(resp)
			}
		}
	case pb.MsgSnapStatus:
		// 检测Follower节点对应的状态是否为ProgressStateSnapshot
		if pr.State != tracker.StateSnapshot {
			return nil
		}
		if !m.Reject {	// 之前的发送MsgSnap消息时出现异常
			pr.BecomeProbe()
			r.logger.Debugf("%x snapshot succeeded, resumed sending replication messages to %x [%s]", r.id, m.From, pr)
		} else {
			// 发送MsgSnap消息失败，这里会清空对反Progress.PendingSnapshot字段
			pr.PendingSnapshot = 0
			pr.BecomeProbe()
			r.logger.Debugf("%x snapshot failed, resumed sending replication messages to %x [%s]", r.id, m.From, pr)
		}
		// 暂停Leader节点向该Follower节，点继续发送消息，如采发送MsgSnap消息成功了，
		// 则待Leader节点收到相应的响应消息（MsgAppResp消息），即可继续发送后续MsgApp消息，
		// 如果发送MsgSnap消息失败了，如lLeader节点会等到收到MsgHeartbeatResp消息时，才会重新开始发送后续消息
		pr.ProbeSent = true
	case pb.MsgUnreachable:
		// 当Follower节点变得不可达，如采继续发送MsgApp消息，则会有大量消息丢失
		if pr.State == tracker.StateReplicate {
			// 将Follower节点对应的Progress实例切换成ProgressStateProbe状态
			pr.BecomeProbe()
		}
		r.logger.Debugf("%x failed to send message to %x because it is unreachable [%s]", r.id, m.From, pr)
	case pb.MsgTransferLeader:
		if pr.IsLearner {
			r.logger.Debugf("%x is learner. Ignored transferring leadership", r.id)
			return nil
		}
		// 在MsgTransferLeader消息中，From字段记录了此次Leader节点迁移操作的目标Follower节点ID
		leadTransferee := m.From
		// 检测当前是否有一次未处理完的Leader节点转移操作
		lastLeadTransferee := r.leadTransferee
		if lastLeadTransferee != None {
			// 目标节点相同，则忽略此次Leader迁移操作
			if lastLeadTransferee == leadTransferee {
				r.logger.Infof("%x [term %d] transfer leadership to %x is in progress, ignores request to same node %x",
					r.id, r.Term, leadTransferee, leadTransferee)
				return nil
			}
			// 若目标节点不同，则清空上次记录的ID｛即raft.leadTransferee)
			r.abortLeaderTransfer()
			r.logger.Infof("%x [term %d] abort previous transferring leadership to %x", r.id, r.Term, lastLeadTransferee)
		}
		// 目标节点已经是Leader节点，放弃此次迁移操作
		if leadTransferee == r.id {
			r.logger.Debugf("%x is already leader. Ignored transferring leadership to self", r.id)
			return nil
		}
		r.logger.Infof("%x [term %d] starts to transfer leadership to %x", r.id, r.Term, leadTransferee)
		// Leader迁移操作应该在electionTirneout时间内完成，这里会重直选举计时器。
		r.electionElapsed = 0
		// 记录此次Leader节点迁移的目标节点ID
		r.leadTransferee = leadTransferee
		// ／检测目标Follower节点是否与当前Leader节点的raftLog是否完全一致
		if pr.Match == r.raftLog.lastIndex() {
			// 向目标Follower节点发送MsgTirneoutNow消息，这会导致Follower节点的选举计时器立即过期，并发起新一轮选举
			r.sendTimeoutNow(leadTransferee)
			r.logger.Infof("%x sends MsgTimeoutNow to %x immediately as %x already has up-to-date log", r.id, leadTransferee, leadTransferee)
		} else {
			// 如采raftLog中的Entry记录没有完全匹配，则Leader节点通过发送MsgApp消息向目标节点进行复制
			r.sendAppend(leadTransferee)
		}
	}
	return nil
}

// PreCandidate节点会收到集群中其他节点返回的MsgPreVoteResp消息，
// 其中的Term字段与PreCandidate节点的Term值相同
func stepCandidate(r *raft, m pb.Message) error {
	var myVoteRespType pb.MessageType
	if r.state == StatePreCandidate {	// 根据当前节点的状态决定其能够处理的选举响应消息的类型
		myVoteRespType = pb.MsgPreVoteResp
	} else {
		myVoteRespType = pb.MsgVoteResp
	}
	switch m.Type {
	case pb.MsgProp:
		r.logger.Infof("%x no leader at term %d; dropping proposal", r.id, r.Term)
		return ErrProposalDropped
	case pb.MsgApp:
		r.becomeFollower(m.Term, m.From) // 始终为m.Term==r.Term 
		r.handleAppendEntries(m)
	case pb.MsgHeartbeat:
		r.becomeFollower(m.Term, m.From) // 始终为m.Term==r.Term 
		r.handleHeartbeat(m)
	case pb.MsgSnap:
		r.becomeFollower(m.Term, m.From) // 始终为m.Term==r.Term 
		r.handleSnapshot(m)
	case myVoteRespType:	// 处理收到的选举响应消息
		gr, rj, res := r.poll(m.From, m.Type, !m.Reject)	// 记录并统计投票结果
		r.logger.Infof("%x has received %d %s votes and %d vote rejections", r.id, gr, m.Type, rj)
		switch res {	// 得票是否过半数
		case quorum.VoteWon:
			if r.state == StatePreCandidate {	// 对MsgPreVoteResp消息的处理
				// 当PreCandidate节点在预选中收到半数以上的选票之后，会发起正式的选举
				r.campaign(campaignElection)
			} else {
				r.becomeLeader()
				r.bcastAppend()	// 向集群中其他节点广播MsgApp消息
			}
		case quorum.VoteLost:
			// 赞同菜与拒绝祟相等时，无法获取半数以上的票数，当前节点切换成Follower状态，
			// 等待下一轮的选举（或预选）
			r.becomeFollower(r.Term, None)
		}
	case pb.MsgTimeoutNow:
		r.logger.Debugf("%x [term %d state %v] ignored MsgTimeoutNow from %x", r.id, r.Term, r.state, m.From)
	}
	return nil
}

func stepFollower(r *raft, m pb.Message) error {
	switch m.Type {
	case pb.MsgProp:
		if r.lead == None {	//当前集群中没有Leader节点，则忽略该MsgProp消息
			r.logger.Infof("%x no leader at term %d; dropping proposal", r.id, r.Term)
			return ErrProposalDropped
		} else if r.disableProposalForwarding {
			r.logger.Infof("%x not forwarding to leader %x at term %d; dropping proposal", r.id, r.lead, r.Term)
			return ErrProposalDropped
		}
		m.To = r.lead	// 将消息的To字段设立为当前Leader节点的id
		r.send(m)	// 将MsgProp消息发送到当前的Leader节点
	case pb.MsgApp:
		r.electionElapsed = 0	// 重直选举计算器，防止当前Follower发起新一轮选举
		r.lead = m.From	// ／设立raft.Lead记录，保存当前集群的Leader节点ID
		// 将MsgApp消息中携带的Entry记录追加到raftLog中，并且向Leader节点发送MsgAppResp消息，响应此次MsgApp消息
		r.handleAppendEntries(m)
	case pb.MsgHeartbeat:	// MsgHeartbeat 消息的处理
		r.electionElapsed = 0	// 重豆选举计时器，防止当前Follower节点发起新一轮选举
		r.lead = m.From	// 设置Leader节点的id
		// 其中会修改raft.committed字段的值（放心，在Leader发送消息时，
		// 已经确定了当前Follower节点有committed之前的全部日志记录），
		// 然后发送MsgHeartbeatResp类型肖息，响应此次心跳
		r.handleHeartbeat(m)
	case pb.MsgSnap:
		r.electionElapsed = 0	// 重置raft.electionElapsed，防止发生选举
		r.lead = m.From	// 设置Leader的id
		r.handleSnapshot(m)	// 通过MsgSnap消息中的快照数据，重建当前节点的raftLog
	case pb.MsgTransferLeader:	// Follower节点直接将MsgTransferLeader消息转给Leader节点
		if r.lead == None {
			r.logger.Infof("%x no leader at term %d; dropping leader transfer msg", r.id, r.Term)
			return nil
		}
		m.To = r.lead
		r.send(m)
	case pb.MsgTimeoutNow:
		r.logger.Infof("%x [term %d] received MsgTimeoutNow from %x and starts an election to get leadership.", r.id, r.Term, m.From)
		// 领导层调动永远不使用预先投票，即使r.preVote为真；我们
		// 知道我们没有从分区恢复，因此不需要额外的往返。
		r.hup(campaignTransfer)
	case pb.MsgReadIndex:
		if r.lead == None {	// 检测当前集群中的Leader节点
			r.logger.Infof("%x no leader at term %d; dropping index reading msg", r.id, r.Term)
			return nil
		}
		m.To = r.lead
		r.send(m)	// 将MsgReadindex消息转发给当前的Leader节点，
	case pb.MsgReadIndexResp:
		if len(m.Entries) != 1 {	// 检测MsgReadindexResp消息的合法性（
			r.logger.Errorf("%x invalid format of MsgReadIndexResp from %x, entries count: %d", r.id, m.From, len(m.Entries))
			return nil
		}
		// 将MsgReadindex消息对应的消息ID以及己提交位置（raftLog. committed）封装成ReadState实例，
		// 并添加到raft.readStates中，等待其他goroutine来处理
		r.readStates = append(r.readStates, ReadState{Index: m.Index, RequestCtx: m.Entries[0].Data})
	}
	return nil
}

// raft.handleAppendEntries（）方法首先会检测MsgApp消息中携带的Entry记录是否合法
// ，然后将这些Entry记录追加到raftLog中，最后创建相应的MsgAppResp消息。
// 当Follower节点或Candidate节点需要向raftLog中追加Entry记录时，会通过raft.handleAppendEntries()
// 方法调用raftLog.maybeAppend（）方法完成追加Entry记录的功能
func (r *raft) handleAppendEntries(m pb.Message) {
	// m. Index表示leader发送给follower的上一条日志的索引位置，
	// 如果Follower节点在Index位置的Entry记录已经提交过了，
	// 则不能进行追加操作，在Raft协议中，己提交的记录不能被覆盖，
	// 所以Follower节点会将其committed位置通过MsgAppResp消息（Index字段）通知Leader节点
	if m.Index < r.raftLog.committed {
		r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: r.raftLog.committed})
		return
	}

	// 尝试将消息携带的Entry记录追加到raftLog中
	if mlastIndex, ok := r.raftLog.maybeAppend(m.Index, m.LogTerm, m.Commit, m.Entries...); ok {
		// 如果追加成功，则将最后一条记录的索引位通过MsgAppResp消息返回给Leader节点，
		// 这样Leader节点就可以根据此值更新其对应的Next和Match值
		r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: mlastIndex})
	} else {
		r.logger.Debugf("%x [logterm: %d, index: %d] rejected MsgApp [logterm: %d, index: %d] from %x",
			r.id, r.raftLog.zeroTermOnErrCompacted(r.raftLog.term(m.Index)), m.Index, m.LogTerm, m.Index, m.From)

		hintIndex := min(m.Index, r.raftLog.lastIndex())
		hintIndex = r.raftLog.findConflictByTerm(hintIndex, m.LogTerm)
		hintTerm, err := r.raftLog.term(hintIndex)
		if err != nil {
			panic(fmt.Sprintf("term(%d) must be valid, but got %v", hintIndex, err))
		}
		// 如果追加记录失败，则将失败信息返回给Leader节点（即MsgAppResp消息的Reject字段为true),
		// 同时返回的还有一些才是示信息（RejectHint字段保存了当前节点raftLog中最后一条记录的索引）
		r.send(pb.Message{
			To:         m.From,
			Type:       pb.MsgAppResp,
			Index:      m.Index,
			Reject:     true,
			RejectHint: hintIndex,
			LogTerm:    hintTerm,
		})
	}
}

func (r *raft) handleHeartbeat(m pb.Message) {
	// 根据MsgHeartbeat消息的Commit字段，更新raftLog中记录的己提交位置，注意，在Leader节点
	// 发送MsgHeartbeat消息时，已经确定了当前Follower节点中raftLog.committed字段的合适位置
	r.raftLog.commitTo(m.Commit)
	// 发送MsgHeartbeatResp消息，响应心跳，
	r.send(pb.Message{To: m.From, Type: pb.MsgHeartbeatResp, Context: m.Context})
}

// 读取MsgSnap消息中的快照数据，并重建当前节点的raftLog
func (r *raft) handleSnapshot(m pb.Message) {
	// 获取快照数据的元数据
	sindex, sterm := m.Snapshot.Metadata.Index, m.Snapshot.Metadata.Term
	if r.restore(m.Snapshot) {	// 返回值表示是否通过快照数据进行了重建
		r.logger.Infof("%x [commit: %d] restored snapshot [index: %d, term: %d]",
			r.id, r.raftLog.committed, sindex, sterm)
		// 向Leader节点返回MsgAppResp消息（Reject始终为false）。
		// 该MsgAppResp消息作为MsgSnap消息的响应，与MsgApp消息并无差别，
		r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: r.raftLog.lastIndex()})
	} else {
		r.logger.Infof("%x [commit: %d] ignored snapshot [index: %d, term: %d]",
			r.id, r.raftLog.committed, sindex, sterm)
		r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: r.raftLog.committed})
	}
}

// 调用raftLog中相应的方法进行检测和重建
func (r *raft) restore(s pb.Snapshot) bool {
	// 判断消息中的时效性
	if s.Metadata.Index <= r.raftLog.committed {
		return false
	}
	// 判断当前节点是否是Follower
	if r.state != StateFollower {
		r.logger.Warningf("%x attempted to restore snapshot as leader; should never happen", r.id)
		r.becomeFollower(r.Term+1, None)
		return false
	}

	found := false
	cs := s.Metadata.ConfState

	for _, set := range [][]uint64{
		cs.Voters,
		cs.Learners,
		cs.VotersOutgoing,
	} {
		for _, id := range set {
			if id == r.id {
				found = true
				break
			}
		}
		if found {
			break
		}
	}
	if !found {
		r.logger.Warningf(
			"%x attempted to restore snapshot but it is not in the ConfState %v; should never happen",
			r.id, cs,
		)
		return false
	}

	// 现在开始实际恢复。
	if r.raftLog.matchTerm(s.Metadata.Index, s.Metadata.Term) {
		r.logger.Infof("%x [commit: %d, lastindex: %d, lastterm: %d] fast-forwarded commit to snapshot [index: %d, term: %d]",
			r.id, r.raftLog.committed, r.raftLog.lastIndex(), r.raftLog.lastTerm(), s.Metadata.Index, s.Metadata.Term)
		r.raftLog.commitTo(s.Metadata.Index)
		return false
	}

	r.raftLog.restore(s)

	// 重置配置并重新添加（可能已更新的）对等点。
	r.prs = tracker.MakeProgressTracker(r.prs.MaxInflight)
	cfg, prs, err := confchange.Restore(confchange.Changer{
		Tracker:   r.prs,
		LastIndex: r.raftLog.lastIndex(),
	}, cs)

	if err != nil {
		// 这永远不会发生。要么我们的配置更改
		// 处理中存在错误，要么客户端破坏了配置更改。
		panic(fmt.Sprintf("unable to restore config %+v: %s", cs, err))
	}

	assertConfStatesEquivalent(r.logger, cs, r.switchToConfig(cfg, prs))

	pr := r.prs.Progress[r.id]
	pr.MaybeUpdate(pr.Next - 1) // TODO（tbg）：这是未经测试的，可能是不需要的

	r.logger.Infof("%x [commit: %d, lastindex: %d, lastterm: %d] restored snapshot [index: %d, term: %d]",
		r.id, r.raftLog.committed, r.raftLog.lastIndex(), r.raftLog.lastTerm(), s.Metadata.Index, s.Metadata.Term)
	return true
}

// promotable表示状态机是否可以升级为leader，
// 当其自己的id在进程列表中时为true。
func (r *raft) promotable() bool {
	pr := r.prs.Progress[r.id]
	return pr != nil && !pr.IsLearner && !r.raftLog.hasPendingSnapshot()
}

func (r *raft) applyConfChange(cc pb.ConfChangeV2) pb.ConfState {
	cfg, prs, err := func() (tracker.Config, tracker.ProgressMap, error) {
		changer := confchange.Changer{
			Tracker:   r.prs,
			LastIndex: r.raftLog.lastIndex(),
		}
		if cc.LeaveJoint() {
			return changer.LeaveJoint()
		} else if autoLeave, ok := cc.EnterJoint(); ok {
			return changer.EnterJoint(autoLeave, cc.Changes...)
		}
		return changer.Simple(cc.Changes...)
	}()

	if err != nil {
		// TODO（tbg）：将错误返回给调用方。
		panic(err)
	}

	return r.switchToConfig(cfg, prs)
}

// switchToConfig重新配置此节点以使用提供的配置。它更新内存中的状态，并在必要时执行额外的操作，如对删除节点或更改的仲裁作出反应。
// 
// 输入通常来自恢复ConfState或应用ConfChange。
func (r *raft) switchToConfig(cfg tracker.Config, prs tracker.ProgressMap) pb.ConfState {
	r.prs.Config = cfg
	r.prs.Progress = prs

	r.logger.Infof("%x switched to configuration %s", r.id, r.prs.Config)
	cs := r.prs.ConfState()
	pr, ok := r.prs.Progress[r.id]

	// 更新节点本身是否为学习者，删除
	// 节点时重置为false。
	r.isLearner = ok && pr.IsLearner

	if (!ok || r.isLearner) && r.state == StateLeader {
		// 此节点是领导者，已被删除或降级。在撰写本文时，我们阻止降职，但假设我们处理降职的方式与罢免领导人：下一届任期。
		// 
		// TODO（待定）：下台（为了理智）并要求与
		// 最匹配的追随者暂停（以避免中断）。这可能仍然会放弃一些
		// 提案，但总比什么都没有好。
		// 
		// TODO（tbg）：测试此分支。在撰写本文时，它还未经过测试。
		return cs
	}

	// 仅当此节点是前导节点且存在其他节点时，其余步骤才有意义。
	if r.state != StateLeader || len(cs.Voters) == 0 {
		return cs
	}

	if r.maybeCommit() {
		// 如果配置更改意味着现在提交更多条目，则
		// 广播/附加到更新配置中的所有人。
		r.bcastAppend()
	} else {
		// 否则仍会探测新添加的副本；没有理由让他们等待心跳间隔（或下一个传入的
		// 提案）。
		r.prs.Visit(func(id uint64, pr *tracker.Progress) {
			r.maybeSendAppend(id, false /* sendIfEmpty */)
		})
	}
	// 如果领导层受让人被免职或降级，则中止领导层转移。
	if _, tOK := r.prs.Config.Voters.IDs()[r.leadTransferee]; !tOK && r.leadTransferee != 0 {
		r.abortLeaderTransfer()
	}

	return cs
}

func (r *raft) loadState(state pb.HardState) {
	if state.Commit < r.raftLog.committed || state.Commit > r.raftLog.lastIndex() {
		r.logger.Panicf("%x state.commit %d is out of range [%d, %d]", r.id, state.Commit, r.raftLog.committed, r.raftLog.lastIndex())
	}
	r.raftLog.committed = state.Commit
	r.Term = state.Term
	r.Vote = state.Vote
}

// pastElectionTimeout在r时返回真值。electioneFassed大于或等于
// 1713
func (r *raft) pastElectionTimeout() bool {
	return r.electionElapsed >= r.randomizedElectionTimeout
}

func (r *raft) resetRandomizedElectionTimeout() {
	r.randomizedElectionTimeout = r.electionTimeout + globalRand.Intn(r.electionTimeout)
}

func (r *raft) sendTimeoutNow(to uint64) {
	r.send(pb.Message{To: to, Type: pb.MsgTimeoutNow})
}

func (r *raft) abortLeaderTransfer() {
	r.leadTransferee = None
}

// 如果对等方在其期限内提交了条目，则CommitteedEntryCurrentTerm返回true。
func (r *raft) committedEntryInCurrentTerm() bool {
	return r.raftLog.zeroTermOnErrCompacted(r.raftLog.term(r.raftLog.committed)) == r.Term
}

// responseToReadIndexReq为'req'构造一个响应。如果'req'来自对等
// 本身，将返回一个空值。
func (r *raft) responseToReadIndexReq(req pb.Message, readIndex uint64) pb.Message {
	if req.From == None || req.From == r.id {
		// 根据MsgRead工ndex消息的From字段，判断该MsgReadindex消息是否为Follower
		// 节点转发到Leader节点的消息
		// 如采是客户端直接发送到Leader节点的消息，则将MsgReadindex消息对应的已提交往置
		// 以及其消息ID封装成ReadState实例，添加到raft.readStates中保存。后续会有
		// 其他goroutine读取该数组，并对相应的MsgReadindex消息进行响应
		r.readStates = append(r.readStates, ReadState{
			Index:      readIndex,
			RequestCtx: req.Entries[0].Data,
		})
		return pb.Message{}
	}
	// 如采是其他Follower节点转发到Leader节点的MsgReadindex消息，则Leader节点
	// 会向Follower节点返回相应的MsgReadindexResp消息，并由Follower节点响应Client
	return pb.Message{
		Type:    pb.MsgReadIndexResp,
		To:      req.From,
		Index:   readIndex,
		Entries: req.Entries,
	}
}

// increaseUncommittedSize计算建议条目的大小，
// 确定它们是否会将leader推过其maxUncommittedSize限制。
// 如果新条目将超过限制，则该方法返回false。否则，
// 记录未提交项大小的增加，该方法返回
// true。
// 
// 永远不会拒绝空有效负载。这既用于在新领导任期内追加一个空
// 条目，也用于留下一个联合配置。
func (r *raft) increaseUncommittedSize(ents []pb.Entry) bool {
	var s uint64
	for _, e := range ents {
		s += uint64(PayloadSize(e))
	}

	if r.uncommittedSize > 0 && s > 0 && r.uncommittedSize+s > r.maxUncommittedSize {
		// 如果木筏日志的未提交尾部为空，则允许任何大小的
		// 建议。否则，请限制
		// 日志的未提交尾部的大小，并放弃任何会使该大小超过该限制的建议。
		// 注意添加的要求s>0，该要求用于确保
		// 将单个空条目添加到日志始终成功，用于复制新领导的初始空条目，以及
		// 自动离开关节配置。
		return false
	}
	r.uncommittedSize += s
	return true
}

// 通过减少未提交的条目大小限制，减少新提交的条目的未提交帐户大小。
func (r *raft) reduceUncommittedSize(ents []pb.Entry) {
	if r.uncommittedSize == 0 {
		// 不跟踪或执行限制的追随者的快速路径。
		return
	}

	var s uint64
	for _, e := range ents {
		s += uint64(PayloadSize(e))
	}
	if s > r.uncommittedSize {
		// 未承诺尺寸可能会低估未承诺筏的尺寸
		// 木尾，但决不会高估。在0处饱和，而不是
		// 允许溢出。
		r.uncommittedSize = 0
	} else {
		r.uncommittedSize -= s
	}
}

func numOfPendingConf(ents []pb.Entry) int {
	n := 0
	for i := range ents {
		if ents[i].Type == pb.EntryConfChange || ents[i].Type == pb.EntryConfChangeV2 {
			n++
		}
	}
	return n
}

func releasePendingReadIndexMessages(r *raft) {
	if !r.committedEntryInCurrentTerm() {
		r.logger.Error("pending MsgReadIndex should be released only after first commit in current term")
		return
	}

	msgs := r.pendingReadIndexMessages
	r.pendingReadIndexMessages = nil

	for _, m := range msgs {
		sendMsgReadIndexResponse(r, m)
	}
}

func sendMsgReadIndexResponse(r *raft, m pb.Message) {
	switch r.readOnly.option {
	case ReadOnlySafe:
		// 记录当前节点的raftLog.committed字段值，lip己提交的位置
		r.readOnly.addRequest(r.raftLog.committed, m)
		r.readOnly.recvAck(r.id, m.Entries[0].Data)
		// 向集群中其他节点发送MsgHeartbeat消息，此次发送的MsgHeartbeat消息中会将addRequest()生成ID作为Context宇段的值。
		r.bcastHeartbeatWithCtx(m.Entries[0].Data)
	case ReadOnlyLeaseBased:
		if resp := r.responseToReadIndexReq(m, r.raftLog.committed); resp.To != None {
			r.send(resp)
		}
	}

	// raft.readOnly字段（*readOnly类型），
	// 该字段的主要作用就是批量处理只读请求。
	// 结构体readOnly中的字段含义如下：
	// option ( ReadOnlyOption类型）：当前只读请求的处理模式,有ReadOnlySafe和ReadOnlyLeaseBased两种模式。
	// pendingReadlndex ( map[string]*readlndexStatus类型）：在etcd服务端收到MsgReadindex消息时，
	// 会为期创建一个消息ID（该ID是唯一的），并作为MsgReadindex消息的第一条Entry记录。
	// 在pendingReadlndex中记录了消息ID与对应请求的readindexStatus实例的映射。
	// readlndexStatus中的字段如下:
	// 	req( pb.Message类型）：记录了对应的MsgReadlndex请求
	// 	index( uint64类型）：该MsgReadindex请求到达时，对应的己提交位置（即raftLog.committed)
	// 	acks(map[uint64]struct｛｝类型）：记录了该MsgReadindex相关的MsgHeartbeatrResp响应信息。
	// readlndexQueue ( [)string类型）：记录了MsgReadindex请求对应的消息ID。
}
